# coding: utf-8
from traceback import print_exc, format_exc
from collections import deque
from typing import Callable, Union, Any

from PySide6.QtCore import QThread, Signal, QObject, QThreadPool
from loguru import logger

from ..widgets import Alert
from ...common import signalBus, ImageHelper, create_session


class RequestHandler:
    def __init__(self, func: Callable, *args, slot: Callable = None, **kwargs):
        self.func = func
        self.slot = slot
        self.args = args
        self.kwargs = kwargs


class ResponseHandler:
    def __init__(self, slot: Callable, result: Any = None, error: Union[Exception, str] = None):
        self.slot = slot
        self.result = result
        if isinstance(error, Exception):
            error = str(error)
        elif error is None:
            error = ""
        self.error = error


class EventSignalBus(QObject):
    fetchDataSignal = Signal(RequestHandler)
    resultSignal = Signal(ResponseHandler)


class QueueThread(QThread):
    result = Signal(ResponseHandler)
    failed = Signal(object)  # 返回失败信息信号

    def __init__(self, parent=None):
        super().__init__(parent)
        self.tasks = deque()

    def handleTask(self, task: RequestHandler):
        result = task.func(*task.args, **task.kwargs)
        return result

    def handleTaskError(self, task: RequestHandler, error: Exception):
        print_exc()

    def run(self):
        while self.tasks:
            task = self.tasks.popleft()
            try:
                result = self.handleTask(task)
                error = None
            except Exception as e:
                result = None
                error = str(e)
                self.handleTaskError(task, e)
                self.failed.emit(error)
            self.result.emit(ResponseHandler(task.slot, result=result, error=error))

    def addTask(self, task: RequestHandler):
        self.tasks.append(task)
        if not self.isRunning():
            self.start()


class QueueThreadPoolWorker(QThread):
    result = Signal(ResponseHandler)
    failed = Signal(object)  # 返回失败信息信号

    def __init__(self, parent=None):
        super().__init__(parent)
        self.tasks = deque()
        self.pool = QThreadPool(self)

    def run(self):
        while self.tasks:
            task = self.tasks.popleft()
            self.pool.start(lambda: self.handleTask(task))

    def handleTask(self, task: RequestHandler):
        try:
            result = task.func(*task.args, **task.kwargs)
            error = None
        except Exception as e:
            result = None
            error = str(e)
            print_exc()
            self.failed.emit(error)
        self.result.emit(ResponseHandler(task.slot, result=result, error=error))

    def addTask(self, task: RequestHandler):
        self.tasks.append(task)
        if not self.isRunning():
            self.start()


class QueueThreadManager(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.queue_thread = QueueThread(self)
        self.loadImagesThread = QueueThreadPoolWorker(self)
        self.dbSession = create_session()

        self.queue_thread.failed.connect(self.handle_error)
        self.queue_thread.result.connect(self.handle_response)
        self.loadImagesThread.failed.connect(self.handle_error)
        self.loadImagesThread.result.connect(self.handle_response)

        self.queue_thread.started.connect(signalBus.threadStartSignal)
        self.queue_thread.finished.connect(signalBus.threadFinishSignal)

    def handle_response(self, response: ResponseHandler):
        try:
            if response.slot:
                result = response.result
                if result:
                    response.slot(result)
                else:
                    response.slot()
                    logger.warning(f"返回值为空: {response.error}")
        except RuntimeError as e:
            logger.warning(f"槽位错误: {e}")

    def handle_error(self, error: str):
        logger.error(f"失败: {error}")
        Alert.error(error)

    def send_function(self, func: Callable, *args, slot: Callable = None, **kwargs):
        request = RequestHandler(func, *args, slot=slot, **kwargs)
        self.queue_thread.addTask(request)

    def load_image(self, *args, slot: Callable = None, **kwargs):
        request = RequestHandler(ImageHelper.get_image_from_url, *args, slot=slot, **kwargs)
        self.loadImagesThread.addTask(request)

    def sqlRequest(self, func: Callable, *args, slot: Callable = None):
        request = RequestHandler(func, *args, session=self.dbSession, slot=slot)
        self.loadImagesThread.addTask(request)

    def close(self):
        self.queue_thread.terminate()
        self.loadImagesThread.terminate()
        self.dbSession.close()
